/***************************************************************************
 *   Copyright (C) 2009 by Marian Skvarek   *
 *   skvarek.marian@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A TrackICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "CEasyScoreExporter.h"
#include "CScore.h"
#include "CPart.h"
#include "CPartFactory.h"
#include "CTrack.h"
#include "CBar.h"
#include "CNote.h"
#include "CPause.h"
#include "CGlobals.h"
#include "CUtils.h"

#include <QtXml/QDomDocument>
#include <QFile>
#include <QTextStream>
#include <QLinkedList>

using namespace easyscore::core;
using namespace easyscore::utils;
using namespace easyscore::globals;

namespace easyscore
{

    namespace components
    {

        namespace io
        {

            //Constructors, Initialization & Destructors

            CEasyScoreExporter::CEasyScoreExporter()
            {

            }

            void CEasyScoreExporter::initialize ()
            {

            }

            void CEasyScoreExporter::clear()
            {

            }

            CEasyScoreExporter::~CEasyScoreExporter()
            {

            }

            bool CEasyScoreExporter::exportScore(CScore * score,QFile &file)
            {
                QDomDocument doc;

                QDomProcessingInstruction instr = doc.createProcessingInstruction("xml", "version='1.0' encoding='UTF-8'");
                doc.appendChild(instr);
                QDomElement scoreEl = doc.createElement("score");
                doc.appendChild(scoreEl);
                scoreEl.setAttribute("name",score->getSongname());
                scoreEl.setAttribute("author",score->getAuthorName());
                scoreEl.setAttribute("tn",score->getTime().getNumerator());
                scoreEl.setAttribute("td",score->getTime().getDenominator());
                scoreEl.setAttribute("tempo",score->getTempo().getValue());

                foreach(CPart *part, score->getParts())
                {
                    QDomElement partEl = doc.createElement("part");
                    partEl.setAttribute("name",part->getName());

                    foreach(CTrack *track, part->getTracks())
                    {
                        QDomElement trackEl = doc.createElement("track");
                        //trackEl.setAttribute("clef",clefToString(track->getClef().getClef()));
                        trackEl.setAttribute("id",track->getID());

                        foreach(CBar *bar, track->getBars())
                        {
                            QDomElement barEl = doc.createElement("bar");

                            foreach(CSymbol *symbol, bar->getSymbols())
                            {
                                QDomElement symbolEl = doc.createElement("symbol");
                                symbolEl.setAttribute("value",symbol->getValue());
                                if(symbol->isRest())
                                {
                                    symbolEl.setAttribute("type","rest");
                                }
                                else
                                {
                                    symbolEl.setAttribute("type","noteGroup");

                                    CNoteGroup *ng = (CNoteGroup *) symbol;
                                    foreach(CNote *note,ng->getNotes())
                                    {
                                        QDomElement noteEl = doc.createElement("note");
                                        noteEl.setAttribute("position",note->getPosition());
                                        noteEl.setAttribute("accidental",accidentalToString(note->getAccidental()));

                                        symbolEl.appendChild(noteEl);
                                    }

                                }

                                barEl.appendChild(symbolEl);
                            }

                            trackEl.appendChild(barEl);
                        }

                        partEl.appendChild(trackEl);

                    }


                    scoreEl.appendChild(partEl);
                }

                //try to open the file
                if(!file.open( QIODevice::WriteOnly ))
                    return false;

                //write to file
                QTextStream out(&file);
                out << doc.toString();
                file.close();

                return true;
            }

            bool CEasyScoreExporter::importScore(CScore * score,QFile &file)
            {
                QDomDocument doc;
                QString errorStr;
                int errorLine;
                int errorColumn;

                if(!doc.setContent(&file, true, &errorStr, &errorLine, &errorColumn))
                    return false;

                QDomNodeList scores = doc.elementsByTagName("score");
                if(scores.count() != 1)
                    return false;

                if(!scores.at(0).isElement())
                    return false;

                QDomElement scoreEl = scores.at(0).toElement();

                if( scoreEl.hasAttribute("name") )
                    score->setSongName(scoreEl.attribute("name"));
                else
                    score->setSongName("Untitled");

                if( scoreEl.hasAttribute("author") )
                    score->setAuthorName(scoreEl.attribute("author"));
                else
                    score->setAuthorName("Unknown author");

                int tn;
                if( scoreEl.hasAttribute("tn") )
                    tn = scoreEl.attribute("tn").toInt();
                else
                    tn = 4;

                int td;
                if( scoreEl.hasAttribute("td") )
                    td = scoreEl.attribute("td").toInt();
                else
                    td = 4;

                score->setTime(CFraction(tn,td));

                int tempo;
                if( scoreEl.hasAttribute("tempo") )
                    tempo = scoreEl.attribute("tempo").toInt();
                else
                    tempo = 60;

                score->setTempo(CTempo(tempo));

                //TODO
                //score-initialize();

                QDomNodeList parts = doc.elementsByTagName("part");
                for(int i = 0; i< parts.count(); ++i)
                {
                    QDomNode partNode = parts.at(i);

                    if(!partNode.isElement())
                        continue;

                    QDomElement partEl = partNode.toElement();

                    if(!partEl.hasAttribute("name"))
                        continue;

                    CPart *part = CPartFactory::createPart(partEl.attribute("name"));
                    score->addPart(part);
                    part->initialize();

                    QDomNodeList tracks = partEl.elementsByTagName("track");
                    for(int j = 0;j < tracks.count(); ++j)
                    {
                        QDomNode trackNode = tracks.at(j);

                        if(!trackNode.isElement())
                            continue;

                        QDomElement trackEl = trackNode.toElement();

                        if(!trackEl.hasAttribute("id"))
                            continue;

                        int id = trackEl.attribute("id").toInt();

                        CTrack *track = part->getTrackByID(id);
                        track->getBars().clear();


                        QDomNodeList bars = trackEl.elementsByTagName("bar");
                        int barCount = bars.count();
                        for(int k = 0;k < barCount; ++k)
                        {
                            QDomNode barNode = bars.at(k);
                            if(!barNode.isElement())
                                continue;

                            QDomElement barEl = barNode.toElement();

                            CBar *bar = track->addBar();
                            bar->clearSymbols();

                            QDomNodeList symbols = barEl.elementsByTagName("symbol");
                            int symbolCount = symbols.count();
                            for(int l = 0;l < symbolCount; ++l)
                            {
                                QDomNode symbolNode = symbols.at(l);
                                if(!symbolNode.isElement())
                                    continue;

                                QDomElement symbolEl = symbolNode.toElement();

                                if(!symbolEl.hasAttribute("value"))
                                    continue;

                                NOTE_VALUE val = symbolEl.attribute("value").toInt();

                                if(!symbolEl.hasAttribute("type"))
                                    continue;

                                if(symbolEl.attribute("type") == "rest")
                                {
                                    CPause *pause = new CPause(val);
                                    pause->initialize();
                                    bar->insertSymbol(pause);
                                }
                                else
                                {
                                    CNoteGroup *noteGroup = new CNoteGroup();
                                    noteGroup->setValue(val);

                                    bar->insertSymbol(noteGroup);

                                    QDomNodeList notes = symbolEl.elementsByTagName("note");
                                    for(int m = 0;m < notes.count(); ++m)
                                    {
                                        QDomNode noteNode = notes.at(m);
                                        if(!noteNode.isElement())
                                            continue;

                                        QDomElement noteEl = noteNode.toElement();

                                        if(!noteEl.hasAttribute("position"))
                                            continue;

                                        CNote *note = noteGroup->insertNote(noteEl.attribute("position").toInt());
                                        note->initialize();
                                        if(!noteEl.hasAttribute("accidental"))
                                            continue;
                                        note->setAccidental(stringToAccidental(noteEl.attribute("accidental")));
                                    }


                                }

                            }

                        }


                    }


                }




                return true;
            }

            QString CEasyScoreExporter::clefToString(EClef clef)
            {
                switch(clef)
                {
                case eClefBass: return "F";
                case eClefTreble: return "G";
                default: return "N";

                }
            }


            QString CEasyScoreExporter::accidentalToString(EAccidental accidental)
            {
                switch(accidental)
                {
                case eAccidentalSharp: return "sharp";
                case eAccidentalFlat: return "flat";
                case eAccidentalNatural: return "natural";
                default: return "none";
                }
            }

            EClef CEasyScoreExporter::stringToClef(QString clef)
            {
                if(clef == "F")
                    return eClefBass;

                if(clef == "G")
                    return eClefTreble;

                return eClefNeutral;

            }

            EAccidental CEasyScoreExporter::stringToAccidental(QString accidental)
            {
                if(accidental == "sharp")
                    return eAccidentalSharp;

                if(accidental == "flat")
                    return eAccidentalFlat;

                if(accidental == "natural")
                    return eAccidentalNatural;

                return eAccidentalNone;
            }
        }

    }

}
